Netflix collects all the data you can imagine and even more! They have millions of dollars of budget every year to invest into new content with the goal of pleasing the customer, increase the watch time, decrease the churn and eventually grow the subscriber base.
Today they need your help visualising the current state of their catalogue.
GOALS 🎯
# importation des librairies/modules
import pandas as pd
import re
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
import plotly.io as pio
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from IPython.display import display, HTML
pd.set_option('display.max_columns', None)
# import des données
dataset = pd.read_csv("./CSV/netflix_titles.csv", encoding='iso-8859-1')
# -- On définit des variables pour une exploration rapide du dataset -- #
# cinq premières lignes du dataset
head_dataset = dataset.head(n=10)
# Statistiques de base du dataset
stats_basiques = dataset.describe()
# Trouver les valeurs manquantes
valeur_manquantes = dataset.isnull().sum()
# identifier le noms des colonnes
nom_des_colonnes = dataset.columns
# identifier le type des colonnes
shape_dataset = dataset.shape
head_dataset
| show_id | type | title | director | cast | country | date_added | release_year | rating | duration | listed_in | description | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | s1 | Movie | Dick Johnson Is Dead | Kirsten Johnson | NaN | United States | September 25, 2021 | 2020 | PG-13 | 90 min | Documentaries | As her father nears the end of his life, filmm... |
| 1 | s2 | TV Show | Blood & Water | NaN | Ama Qamata, Khosi Ngema, Gail Mabalane, Thaban... | South Africa | September 24, 2021 | 2021 | TV-MA | 2 Seasons | International TV Shows, TV Dramas, TV Mysteries | After crossing paths at a party, a Cape Town t... |
| 2 | s3 | TV Show | Ganglands | Julien Leclercq | Sami Bouajila, Tracy Gotoas, Samuel Jouy, Nabi... | NaN | September 24, 2021 | 2021 | TV-MA | 1 Season | Crime TV Shows, International TV Shows, TV Act... | To protect his family from a powerful drug lor... |
| 3 | s4 | TV Show | Jailbirds New Orleans | NaN | NaN | NaN | September 24, 2021 | 2021 | TV-MA | 1 Season | Docuseries, Reality TV | Feuds, flirtations and toilet talk go down amo... |
| 4 | s5 | TV Show | Kota Factory | NaN | Mayur More, Jitendra Kumar, Ranjan Raj, Alam K... | India | September 24, 2021 | 2021 | TV-MA | 2 Seasons | International TV Shows, Romantic TV Shows, TV ... | In a city of coaching centers known to train I... |
| 5 | s6 | TV Show | Midnight Mass | Mike Flanagan | Kate Siegel, Zach Gilford, Hamish Linklater, H... | NaN | September 24, 2021 | 2021 | TV-MA | 1 Season | TV Dramas, TV Horror, TV Mysteries | The arrival of a charismatic young priest brin... |
| 6 | s7 | Movie | My Little Pony: A New Generation | Robert Cullen, José Luis Ucha | Vanessa Hudgens, Kimiko Glenn, James Marsden, ... | NaN | September 24, 2021 | 2021 | PG | 91 min | Children & Family Movies | Equestria's divided. But a bright-eyed hero be... |
| 7 | s8 | Movie | Sankofa | Haile Gerima | Kofi Ghanaba, Oyafunmike Ogunlano, Alexandra D... | United States, Ghana, Burkina Faso, United Kin... | September 24, 2021 | 1993 | TV-MA | 125 min | Dramas, Independent Movies, International Movies | On a photo shoot in Ghana, an American model s... |
| 8 | s9 | TV Show | The Great British Baking Show | Andy Devonshire | Mel Giedroyc, Sue Perkins, Mary Berry, Paul Ho... | United Kingdom | September 24, 2021 | 2021 | TV-14 | 9 Seasons | British TV Shows, Reality TV | A talented batch of amateur bakers face off in... |
| 9 | s10 | Movie | The Starling | Theodore Melfi | Melissa McCarthy, Chris O'Dowd, Kevin Kline, T... | United States | September 24, 2021 | 2021 | PG-13 | 104 min | Comedies, Dramas | A woman adjusting to life after a loss contend... |
dataset['country'].isnull().sum()
831
Cette colonne contient les dates en format chaine de caractères, pour une meilleure manipulation des données on passe au type datetime.
dataset['date_added'] = pd.to_datetime(dataset['date_added'])
Nous allons crée une colonne pour determiner si un film à la caratéristique international.
Un film international est un film qui n'a pas été produit en majorit au Etats-Unis.
"""
On va rajouter une colonne qui va nous permettre de savoir si le film est internationnal ou non
Dans la colonne listed_in il y a les catagories de films 'International Movies' et 'International TV Shows'
"""
# On crée une fonction qui va nous permettre de savoir si le film est international ou non
def is_international(liste_genre):
try:
liste_genre = [genre.strip() for genre in liste_genre.split(',')]
for genre in liste_genre:
if re.match('^International Movies$', genre) or re.match('^International TV Shows$', genre):
return 1
else:
return 0
except:
pass
# On applique la fonction à la colonne listed_in
dataset['is_international'] = dataset['listed_in'].apply(is_international)
dataset['is_international'].value_counts()
# On voit qu'il y a 2430 films internationaux et 5377 films non internationaux
0 7905 1 902 Name: is_international, dtype: int64
"""
La colonne listed_in contient plusieurs genres séprarés par des virgules.
Certains genre sont Movies et TV Show.
Cela donne un contenu de "type" Movie et de genre "Movie" ce qui n'est pas utile et pourrait fausser les résultats.
Une solution serait de supprimer les genres Movie et Tv Show de la colonne listed_in.
Avec la fonction apply, on peut appliquer une fonction à chaque ligne du dataset.
Cette fonction va retirer les genres Movie et Tv Show de la colonne listed_in.
En REGEX, Il est important de chercher les mots Movie/Tv Show entre des caractères spéciaux pour les cibler spécifiquement.
"""
def replace_genre(liste_genre):
# je split la liste des genres par des virgules
genres_a_supprimer = ['^TV Shows$', '^Movies$', "^International Movies$", "^International TV Shows$"]
try:
liste_genre = [genre.strip() for genre in liste_genre.split(',')]
for genre in liste_genre:
for genre_a_supprimer in genres_a_supprimer:
if re.match(genre_a_supprimer, genre):
liste_genre.remove(genre)
# je reconstitue la liste des genres en les séparant par des virgules
liste_genre = ', '.join(liste_genre)
return liste_genre
except:
pass
dataset['listed_in'] = dataset['listed_in'].apply(replace_genre)
dataset_movies = dataset[dataset['type'] == 'Movie']
dataset_tv_shows = dataset[dataset['type'] == 'TV Show']
top_genre_movies = dataset_movies['listed_in'].str.split(',').explode().str.strip().value_counts()
top_genre_tv_shows = dataset_tv_shows['listed_in'].str.split(',').explode().str.strip().value_counts()
html_output = f"<div style='display: flex;'><div style='margin-right: 20px;'>{top_genre_movies.to_frame().to_html()}</div><div>{top_genre_tv_shows.to_frame().to_html()}</div></div>"
display(HTML(html_output))
| listed_in | |
|---|---|
| Dramas | 2427 |
| Comedies | 1674 |
| Documentaries | 869 |
| Action & Adventure | 859 |
| Independent Movies | 756 |
| Children & Family Movies | 641 |
| Romantic Movies | 616 |
| Thrillers | 577 |
| Music & Musicals | 375 |
| Horror Movies | 357 |
| Stand-Up Comedy | 343 |
| Sci-Fi & Fantasy | 243 |
| Sports Movies | 219 |
| Classic Movies | 116 |
| LGBTQ Movies | 102 |
| Anime Features | 71 |
| Cult Movies | 71 |
| Faith & Spirituality | 65 |
| 60 |
| listed_in | |
|---|---|
| TV Dramas | 763 |
| TV Comedies | 581 |
| Crime TV Shows | 470 |
| Kids' TV | 451 |
| Docuseries | 395 |
| Romantic TV Shows | 370 |
| Reality TV | 255 |
| British TV Shows | 253 |
| Anime Series | 176 |
| Spanish-Language TV Shows | 174 |
| TV Action & Adventure | 168 |
| Korean TV Shows | 151 |
| TV Mysteries | 98 |
| Science & Nature TV | 92 |
| TV Sci-Fi & Fantasy | 84 |
| TV Horror | 75 |
| Teen TV Shows | 69 |
| TV Thrillers | 57 |
| Stand-Up Comedy & Talk Shows | 56 |
| Classic & Cult TV | 28 |
| 18 |
fig = make_subplots(rows=2,
cols=1,
subplot_titles=['Nombre de films par genre', 'Nombre de Tv Shows par genre'],
shared_xaxes=False
)
fig.add_trace(go.Bar(x=top_genre_movies.index, y=top_genre_movies.values, name='Movie'), row=1, col=1)
fig.add_trace(go.Bar(x=top_genre_tv_shows.index, y=top_genre_tv_shows.values, name='Tv Show'), row=2, col=1)
fig.update_layout(height=750, width=800)
fig.show()
pays_producteurs = dataset['country'].str.split(',').explode().str.strip().unique()
pays_producteurs = pd.DataFrame(pays_producteurs, columns=['Pays'])
fig = px.choropleth(pays_producteurs, locations="Pays", color_discrete_sequence=["red"], locationmode="country names")
fig.update_layout(title_text='Pays producteur de contenu sur Netflix', title_x=0.5)
fig.show()
volume_production_pays = dataset['country'].str.split(',').explode().str.strip().value_counts()
volume_production_pays
United States 3690
India 1046
United Kingdom 806
Canada 445
France 393
...
Ecuador 1
Armenia 1
Mongolia 1
Bahamas 1
Montenegro 1
Name: country, Length: 123, dtype: int64
fig = go.Figure()
fig.add_trace(go.Bar(x=volume_production_pays.index,
y=volume_production_pays.values,
text=volume_production_pays.values,
texttemplate='%{text:.2s}',
textposition='outside')
)
fig.update_layout(title_text='Volume de production par pays', title_x=0.5)
fig.show()
data = {'Pays': volume_production_pays.index, 'Volume de production': volume_production_pays.values}
df = pd.DataFrame(data)
fig = px.choropleth(df, locations="Pays", color="Volume de production", color_continuous_scale=px.colors.sequential.OrRd, range_color=[1, 500], locationmode="country names")
fig.update_layout(title_text='Production de contenu par pays', title_x=0.5)
fig.show()
# la liste des pays producteurs
liste_country = dataset['country'].dropna().str.split(',').explode().str.strip().unique()
def get_top_genre_movies(liste_country):
liste_resultat = []
for country in liste_country:
dataset_country = dataset[dataset['country'].str.contains(country, na=False)]
dataset_country_movies = dataset_country[dataset_country['type'] == 'Movie']
top_genre_movies = dataset_country_movies['listed_in'].dropna().str.split(',').explode().str.strip().value_counts()
for i in range(len(top_genre_movies)):
liste_resultat.append({"Pays": country, "Genre": top_genre_movies.index[i], "Valeurs": top_genre_movies.values[i]})
return liste_resultat
def get_top_genre_tvshow(liste_country):
liste_resultat = []
for country in liste_country:
dataset_country = dataset[dataset['country'].str.contains(country, na=False)]
dataset_country_tv_show = dataset_country[dataset_country['type'] == 'TV Show']
top_genre_tv_shows = dataset_country_tv_show['listed_in'].dropna().str.split(',').explode().str.strip().value_counts()
for i in range(len(top_genre_tv_shows)):
liste_resultat.append({"Pays": country, "Genre": top_genre_tv_shows.index[i], "Valeurs": top_genre_tv_shows.values[i]})
return liste_resultat
top_genre_country_movie = pd.DataFrame(get_top_genre_movies(liste_country))
top_genre_country_tvshow = pd.DataFrame(get_top_genre_tvshow(liste_country))
# Les résultats contient la somme par genre ce qui fausse le treemap et affiche des genres sans pays associés
# on supprime les lignes en question
for i in range(len(top_genre_country_movie)):
if top_genre_country_movie['Pays'][i] == '':
top_genre_country_movie.drop(i, inplace=True)
for i in range(len(top_genre_country_tvshow)):
if top_genre_country_tvshow['Pays'][i] == '':
top_genre_country_tvshow.drop(i, inplace=True)
labels_m = [genre for genre in top_genre_country_movie['Genre']]
parents_m = ['' for i in range(len(top_genre_country_movie))]
values_m = [valeur for valeur in top_genre_country_movie['Valeurs']]
pays_m = [p for p in top_genre_country_movie['Pays']]
# je créer un DataFrame avec les listes pays, labels, values et parents
data_m = {'Pays': pays_m, 'labels': labels_m, 'values': values_m, 'parents': parents_m}
df_m = pd.DataFrame(data_m)
fig = px.treemap(df_m, path=['Pays', 'labels'], values='values', color='values', color_continuous_scale='RdBu')
fig.update_layout(title_text="Top Movie genre par pays", title_x=0.5)
fig.show()
labels_m = [genre for genre in top_genre_country_tvshow['Genre']]
parents_m = ['' for i in range(len(top_genre_country_tvshow))]
values_m = [valeur for valeur in top_genre_country_tvshow['Valeurs']]
pays_m = [p for p in top_genre_country_tvshow['Pays']]
# je créer un DataFrame avec les listes pays, labels, values et parents
data_m = {'Pays': pays_m, 'labels': labels_m, 'values': values_m, 'parents': parents_m}
df_m = pd.DataFrame(data_m)
fig = px.treemap(df_m, path=['Pays', 'labels'], values='values', color='values', color_continuous_scale='RdBu')
fig.update_layout(title_text="Top TV Show genre par pays", title_x=0.5)
fig.show()
/usr/lib/python3/dist-packages/plotly/express/_core.py:1637: FutureWarning: The frame.append method is deprecated and will be removed from pandas in a future version. Use pandas.concat instead. /usr/lib/python3/dist-packages/plotly/express/_core.py:1637: FutureWarning: The frame.append method is deprecated and will be removed from pandas in a future version. Use pandas.concat instead.
/usr/lib/python3/dist-packages/plotly/express/_core.py:1637: FutureWarning: The frame.append method is deprecated and will be removed from pandas in a future version. Use pandas.concat instead. /usr/lib/python3/dist-packages/plotly/express/_core.py:1637: FutureWarning: The frame.append method is deprecated and will be removed from pandas in a future version. Use pandas.concat instead.
# pour la saisonalité, faire un graphique sur le temps on voit des piques à chaque début de mois
# la date est en datetime de la forme yyyy-mm-dd
#je selectionne les années de 2017 à 2021
liste_years = [2016, 2017, 2018,2019,2020,2021]
liste_month = ["January","February","March","April","May","June","July","August","September","October","November","December"]
# je créer une liste vide qui va contenir les résultats de la boucle
liste_release_per_month = []
# pour chaque année je compte le nombre d'occurence par mois
# pour cela j'utilise une boucle for qui va parcourir la liste des années
# je selectionne les années avec .dt.year ce qui permet de comparer les années dans e dataset avec celle de la liste
# pour chaque année je compte le nombre d'occurence par mois avec .dt.month.value_counts().sort_index()
# cela me permettra de créer un graphique avec plotly ou j'affiche les sorties par mois pour chaque année
for year in liste_years:
dataset_year = dataset[dataset['date_added'].dt.year == year]
result = dataset_year['date_added'].dt.month.value_counts().sort_index()
liste_release_per_month.append({"Year": year, "Release by Month": result})
# je crée un subplots dans lequel je vais afficher les graphiques de 2017 à 2021
fig = make_subplots(rows=3,
cols=2,
shared_xaxes=False
)
# je créer un graphique qui présente les genres les plus populaires par pays en utilisant la liste liste_top_genre_per_country
# je crée un boucle qui va crée des graphiques avec la fonction add_trace dans le subplots.
# la difficiulté =» je dois incrémenter le row et le col pour placer le graphique dans la bonne case pour ne pas les avoirs tous à la suite
# je rajoute une condition qui bascule sur le second fig.add_trace qui incrémente sur la colonne 2 et sur les ligne 1 et 2 de la seconde colonne.
# pour cela le numéro de la row correspond à la valeur i moins trois
# par exemple arrivé à l'itéaration 4 cela donne i(=4) - 3 pur être sur la première ligne de la seconde colonne
for i in range(1, len(liste_release_per_month)+1):
if i <= 3:
fig.add_trace(go.Bar(
x=liste_month,
y=liste_release_per_month[i-1]['Release by Month'].values,
text=liste_release_per_month[i-1]['Release by Month'].values,
texttemplate='%{text:.2s}',
textposition='outside',
name=liste_release_per_month[i-1]['Year']),
row=i,
col=1)
else:
fig.add_trace(go.Bar(
x=liste_month,
y=liste_release_per_month[i-1]['Release by Month'].values,
text=liste_release_per_month[i-1]['Release by Month'].values,
texttemplate='%{text:.2s}',
textposition='outside',
name=liste_release_per_month[i-1]['Year']),
row=i-3,
col=2)
# Personnaliser la mise en page
fig.update_layout(height=1000, width=900)
# je rajoute une légende centré par rapport à la carte
fig.update_layout(title_text='Sorties Netflix par mois (2016-2021)', title_x=0.5)
fig.show()
calendirer_sortie = dataset.groupby('date_added')['show_id'].count()
fig = go.Figure(
data = go.Scatter(
x = calendirer_sortie.index,
y = calendirer_sortie.values,
),
layout = go.Layout(
title = go.layout.Title(text = "Sorties Netflix (2008-2021)", x = 0.5),
xaxis = go.layout.XAxis(title = 'Date à laquelle les sorties ont été effectuées', rangeslider = go.layout.xaxis.Rangeslider(visible = True)),
yaxis = go.layout.YAxis(title = 'Volumz de sortie')
)
)
fig.show(renderer="notebook_connected")